from fastapi.exceptions import (
    HTTPException,
    RequestValidationError,
    ResponseValidationError,
)
from uvicorn.protocols.http.h11_impl import STATUS_PHRASES
from fastapi import FastAPI
from fastapi.requests import Request
from pydantic import (
    ValidationError, PydanticUserError
)
from tortoise.exceptions import (
    MultipleObjectsReturned, DoesNotExist,
    OperationalError
)
import traceback

from common.response.response_code import (
    CustomErrorCode, HttpCode
)
from common.response.http_response import (
    CustomResponse, CustomContent
)
from common.exception.validation_error_msg import (
    CUSTOM_USAGE_ERROR_MESSAGES,
    CUSTOM_VALIDATION_ERROR_MESSAGES
)
from common.log import log
from settings.config import settings

from .errors import CustomException

class SettingNotFound(Exception):
    pass

def _get_exception_code(status_code: int):
    """获取返回状态码, OpenAPI, Uvicorn...
    """
    try:
        STATUS_PHRASES[status_code]
    except Exception:
        code = HttpCode.HTTP_400
    else:
        code = status_code
    return code

async def _validation_exception_handler(request: Request, e: RequestValidationError | ValidationError):
    """数据验证异常处理

    Args:
        e (RequestValidationError | ValidationError): 异常
    
    Returns:

    """
    errors = []
    for error in e.errors():
        custom_msg = CUSTOM_VALIDATION_ERROR_MESSAGES.get(error['type'])
        if custom_msg:
            ctx = error.get('ctx')
            if not ctx:
                error['msg'] = custom_msg
            else:
                error['msg'] = custom_msg.format(**ctx)
                ctx_error = ctx.get('error')
                if ctx_error:
                    error['ctx']['error'] = (
                        ctx_error.__str__().replace("'", '"') if isinstance(ctx_error, Exception) else None
                    )
        errors.append(error)
    if error.get('type') == 'json_invalid':
        message = 'json解析失败'
    else:
        error_input = error.get('input')
        # field = str(error.get('loc')[-1])
        # 检查 loc 是否为空
        if error.get('loc'):
            field = str(error.get('loc')[-1])
        else:
            field = "global_error"
        error_msg = error.get('msg')
        if field == "global_error":
            message = f"Global error: {error_msg}"
        else:
            message = f'{field} {error_msg}，输入：{error_input}' if settings.ENV == 'dev' else error_msg

    
    msg = f'请求参数非法: {message}'
    data = {'errors': errors} if settings.ENV == 'dev' else None
    
    content = CustomContent(
        code=HttpCode.HTTP_422, 
        msg=msg, 
        data=data
    ).to_dict()
    return CustomResponse(status_code=HttpCode.HTTP_422, content=content)
    

def register_exception(app: FastAPI):
    
    @app.exception_handler(MultipleObjectsReturned)
    async def db_multi_objects_exception_handler(request: Request, exc: MultipleObjectsReturned):
        """数据库返回多个记录异常处理

        Args:
            request (Request): 
            exc (MultipleObjectsReturned): 
            
        """
        content = CustomContent(
            code=HttpCode.HTTP_500, 
            msg=str(exc), 
            data=None
        ).to_dict()
        return CustomResponse(
            status_code=HttpCode.HTTP_500,
            content=content,
        )
        
    @app.exception_handler(DoesNotExist)
    async def db_not_found_exception_handler(request: Request, exc: DoesNotExist):
        """数据库无记录异常处理

        Args:
            request (Request): 
            exc (DoesNotExist): 
            
        """
        content = CustomContent(
            code=HttpCode.HTTP_404, 
            msg=str(exc), 
            data=None
        ).to_dict()
        return CustomResponse(
            status_code=HttpCode.HTTP_404,
            content=content,
        )
        
    @app.exception_handler(OperationalError)
    async def db_operation_exception_handler(request: Request, exc: OperationalError):
        """数据库操作异常处理

        Args:
            request (Request): 
            exc (OperationalError): 
            
        """
        content = CustomContent(
            code=HttpCode.HTTP_500, 
            msg=exc.args, 
            data=None
        ).to_dict()
        '''打印堆栈信息'''
        tb = traceback.format_exc()
        log.error(tb)
        return CustomResponse(
            status_code=HttpCode.HTTP_500,
            content=content,
        )
    
    @app.exception_handler(HTTPException)
    async def http_exception_handler(request: Request, exc: HTTPException):
        """全局 HTTP 异常处理

        Args:
            request (Request): 
            exc (HTTPException): 
            
        """
        content = CustomContent(
            code=exc.status_code, 
            msg=exc.detail, 
            data=None
        ).to_dict()
        return CustomResponse(
            status_code=_get_exception_code(exc.status_code),
            content=content,
            headers=exc.headers,
        )

    @app.exception_handler(CustomException)
    async def custom_exception_handler(request: Request, exc: CustomException):
        """自定义通用异常处理

        Args:
            request (Request): 
            exc (CustomException): 
        """
        content = CustomContent(
            code = exc.code,
            msg = exc.msg,
            data = None
        ).to_dict()
        return CustomResponse(content=content, status_code=exc.code)
    
    @app.exception_handler(RequestValidationError)
    async def fastapi_validation_exception_handler(request: Request, exc: RequestValidationError):
        """fastapi 数据验证异常处理

        Args:
            request (Request): 
            exc (HTTPException): 
        """
        return await _validation_exception_handler(request, exc)
    
    @app.exception_handler(ValidationError)
    async def pydantic_validation_exception_handler(request: Request, exc: ValidationError):
        """pydantic 数据验证异常处理

        Args:
            request (Request): 
            exc (ValidationError): 
        """
        return await _validation_exception_handler(request, exc)
    
    @app.exception_handler(PydanticUserError)
    async def pydantic_user_error_handler(request: Request, exc: PydanticUserError):
        """pydantic 用户异常处理

        Args:
            request (Request):
            exc (PydanticUserError):
        """
        content = CustomContent(
            code = HttpCode.HTTP_500,
            msg = CUSTOM_USAGE_ERROR_MESSAGES.get(exc.code),
            data = None
        ).to_dict()
        return CustomResponse(
            status_code=HttpCode.HTTP_500,
            content=content
        )
        
    @app.exception_handler(AssertionError)
    async def assertion_error_handler(request: Request, exc: AssertionError):
        """断言错误处理

        Args:
            request (Request): 
            exc (AssertionError): 
        """
        content = CustomContent(
            code = HttpCode.HTTP_500,
            msg = str(''.join(exc.args) if exc.args else exc.__doc__),
            data = None
        ).to_dict()
        '''打印堆栈信息'''
        tb = traceback.format_exc()
        log.error(tb)
        return CustomResponse(
            status_code=HttpCode.HTTP_500,
            content=content
        )
    
    @app.exception_handler(Exception)   
    async def global_exception_handler(request: Request, exc: Exception):
        """全局异常处理

        Args:
            request (Request):
            exc (Exception):
        """
        content = CustomContent(
            code = HttpCode.HTTP_500,
            msg = str(exc),
            data = None
        ).to_dict()
        '''打印堆栈信息'''
        tb = traceback.format_exc()
        log.error(tb)
        return CustomResponse(
            status_code=HttpCode.HTTP_500,
            content=content
        )
        